元件的概念大概有了部分的了解,同一個元件可以重複利用,但這句話好像有個問題,不同頁面使用相同架構的元件內容肯定需要做替換,這時候就是slot要出場啦!
編譯作用域的意思就是「變數的作用範圍」。
<h1>{{msg}}</h1>
<my-component>{{msg}}</my-component>
const app = Vue.createApp({
data() {
return {
msg: 'Parent'
}
}
});
app.component('my-component', {
template: `<h2>Hello!</h2>`,
data() {
return {
msg: 'Child'
}
},
})
當內外層都有data
時,畫面會出現
my-component
內的msg
不是出現Child而是template
的內容,這表示元件在編譯的時候會直接忽略內容物,直接放入template
中的模板。
對子元件鑽洞,讓外層元件的資料可以放入。
app.component('my-component', {
template: `
<h2>Hello!
<slot></slot>
</h2>`,
data() {
return {
msg: 'Child'
}
},
})
將slot放入template中,可以得出這樣的結果
為什麼slot
內容物不是Child???
slot
特性是保留一個空間可以從外部傳入內容,而子元件本身對slot
並沒有控制權。 ——重新認識 Vue.js
若想要給予子元件一個「預設內容」,可以直接在slot
中放上預設內容。
<my-component></my-component>
app.component('my-component', {
template: `
<h2>Hello!
<slot>預設內容</slot>
</h2>`,
data() {
return {
msg: 'Child'
}
},
})
上面的範例是當只有一個內容物需要做變更的情況,當template內有很多需要換內容的時候就需要幫他們命名,讓資料換到對應的位置。
<light-box>
<template v-slot:header>
<h1>這是header</h1>
</template>
<div>
沒有給名字的slot
</div>
</light-box>
const app = Vue.createApp({
data() {
}
});
app.component('light-box', {
template: `
<div class="lightbox">
<div class="modal-mask" :style="modalStyle">
<div class="modal-container" @click.self="toggleModal">
<div class="modal-body">
<header>
<slot name="header">Default header</slot>
</header>
<hr>
<main>
<slot>Default body</slot>
</main>
<hr>
<footer>
<slot name="footer">Default footer</slot>
</footer>
</div>
</div>
</div>
<button @click="isShow = true">Click</button>
</div>`,
data: () => ({
isShow: false
}),
computed: {
modalStyle() {
return {
'display': this.isShow ? '' : 'none'
}
}
},
methods: {
toggleModal() {
console.log('click')
this.isShow = !this.isShow
}
}
})
方法其實和昨天的is
差不多,在這邊的寫法改為v-slot:[]
,可以自己選擇位置放入更新內容。
<label v-for="opt in options">
<input type="radio" :value="opt" v-model="dynamic_slot_name">{{opt}}
</label>
<light-box>
<template v-slot:[dynamic_slot_name]>
<h1>更新後內容</h1>
</template>
</light-box>
const app = Vue.createApp({
data() {
return {
options: ['header', 'footer', 'default'],
dynamic_slot_name: 'header'
}
}
});
slot資料都是由父層提供,若希望子元素的data也可以顯示就可以用到以下方式
:hello="helloString[lang]"
綁定子元素資料v-slot:default=""{hello}
接收並吐值給下面的{{hello}}
<p>
請選擇
<select v-model="lang">
<option v-for="n in langOptions" :value="n.val">
{{n.name}}
</option>
</select>
</p>
<light-box :lang='lang'>
<!-- 寫法一 -->
<template v-slot:default="props">
{{langOptions.find(d=>d.val===lang)['name']}}
{{props.hello}}
</template>
<!-- 寫法二(解構) -->
<template v-slot:default="{hello}">
{{langOptions.find(d=>d.val===lang)['name']}}
{{hello}}
</template>
</light-box>
const app = Vue.createApp({
data: () => ({
langOptions: [{
name: '繁體中文',
val: 'tw'
}, {
name: 'Deutsch',
val: 'de'
}, {
name: 'English',
val: 'en'
}],
lang: 'tw'
})
});
app.component('light-box', {
template: `
<div class="lightbox">
<div class="modal-mask" :style="modalStyle">
<div class="modal-container" @click.self="toggleModal">
<div class="modal-body">
<slot name="default" :hello="helloString[lang]"></slot>
</div>
</div>
</div>
<button @click="isShow = true">Click</button>
</div>`,
props: {
lang: {
type: String,
default: 'tw'
}
},
data: () => ({
helloString: {
'tw': '哈囉',
'de': 'Hallo',
'en': 'Hello'
},
isShow: false
}),
computed: {
modalStyle() {
return {
'display': this.isShow ? '' : 'none'
}
}
},
methods: {
toggleModal() {
console.log('click')
this.isShow = !this.isShow
}
}
})
這部分的內容有點複雜,所以再次改部分名稱看看到底是怎麼傳資料的。
:hello="helloString[lang]"
中的:hello傳出值後只是個屬性的名稱,所以換成abc也可以{{prop.abc}}
就可以看到值出現,或是用解構方式v-slot:default="{abc}"
控制元件選染的位置,像是上面的範例,因為元件的父層不是body
,所以灰底的遮罩蓋不到整的頁面,teleport to="body"
就可以把她掛載到body
上
<div class="lightbox">
<teleport to="body">
<div class="modal-mask" :style="modalStyle">
<div class="modal-container" @click.self="toggleModal">
<div class="modal-body">
<slot name="default" :abc="helloString[lang]"></slot>
</div>
</div>
</div>
</teleport>
<button @click="isShow = true">Click</button>
</div>
複用元件的好幫手:Vue Slots(v-slot、Scoped Slots)
https://medium.com/unalai/複用元件的好幫手-vue-slot-v-slot-scoped-slots-5364a0048ab7